import logging
import os
import pybroker as pb
from pybroker import Strategy, StrategyConfig
import akshare as ak
import pybroker

# 配置日志记录
log_dir = os.path.join(os.path.expanduser('~'), 'xquant_logs')
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, 'strategy.log')

# 确保日志系统配置正确
logger = logging.getLogger()
logger.handlers = []  # 清除现有handler

handler = logging.FileHandler(log_file)
handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)

print(f"日志文件路径: {log_file}")
logging.info("日志系统初始化完成")
logging.basicConfig(
    filename=log_file,
    level=logging.INFO,
    format='%(asctime)s - %(message)s'
)
logging.info("="*50)
logging.info("Initializing strategy")
import numpy as np
import pandas as pd
import talib
from pybroker.ext.data import AKShare

# 获取沪深300成分股代码
def fetch_hs300_symbols():
    hs300_df = ak.index_stock_cons_sina(symbol="000300")
    symbols = hs300_df['code'].tolist()
    return [f"{s.split('.')[0]}.SH" if s.startswith('6') else f"{s.split('.')[0]}.SZ"
            for s in symbols]

# 创建策略
def create_strategy():
    # 策略配置
    config = StrategyConfig(
        initial_cash=100000,
        max_long_positions=20  # 最大持仓20只股票
    )
    # 使用内置AKShare数据源
    akshare = AKShare()
    strategy = Strategy(
        akshare,
        start_date='20200101',  # 修正日期格式
        end_date='20231231',
        config=config
    )
    
    # 添加沪深300股票池
    symbols = fetch_hs300_symbols()
    logging.info(f"获取到{len(symbols)}只沪深300成分股: {symbols[:5]}...")  # 打印前5个股票代码
    
    pb.indicator('sma_10',talib)
    
    # 定义执行函数
    def execute(ctx):
        symbol = ctx.symbol
        
        # 彻底重构数据验证逻辑
        try:
            # 检查并转换bars数据
            if not hasattr(ctx, 'bars') or ctx.bars is None:
                logging.warning(f"{symbol}: 无bars数据")
                return
                
            # 处理各种可能的bars数据类型
            if isinstance(ctx.bars, int):
                logging.warning(f"{symbol}: bars是整数值 {ctx.bars}")
                return
                
            # 转换为DataFrame
            bars_df = pd.DataFrame(ctx.bars) if not isinstance(ctx.bars, (pd.DataFrame, pd.Series)) else ctx.bars
            
            # 验证数据完整性
            if bars_df.empty or len(bars_df) < 30:
                logging.warning(f"{symbol}: 数据不足 ({len(bars_df)}条)")
                return
                
            # 确保有收盘价数据
            if 'close' not in bars_df.columns:
                logging.warning(f"{symbol}: 缺少close价格数据")
                return
                
            # 记录有效数据
            logging.info(f"{symbol}: 加载{len(bars_df)}条有效数据")
            
            # 提取收盘价并计算技术指标
            close_prices = bars_df['close'].values.astype(np.float64)
            
            # 计算技术指标
            sma10 = talib.SMA(close_prices, timeperiod=10)
            sma30 = talib.SMA(close_prices, timeperiod=30)
            rsi = talib.RSI(close_prices, timeperiod=14)
            upper, middle, lower = talib.BBANDS(close_prices, timeperiod=20)
            
            # 确保有足够数据
            if len(sma10) < 2 or len(sma30) < 2 or len(rsi) < 1:
                return
                
            current_pos = ctx.long_pos()
            close_price = bars_df['close'].iloc[-1]

        except Exception as e:
            logging.error(f"处理股票 {symbol} 数据时出错: {str(e)}")
            return
        
        # 多因子信号系统
        buy_signal = False
        sell_signal = False
        
        # 详细调试输出
        logging.info(f"{symbol} - Close:{close_price:.2f} SMA10:{sma10[-1]:.2f} SMA30:{sma30[-1]:.2f} RSI:{rsi[-1]:.2f}")
        logging.info(f"Bollinger Bands - Upper:{upper[-1]:.2f} Middle:{middle[-1]:.2f} Lower:{lower[-1]:.2f}")
        logging.info(f"Current Position: {current_pos} shares")
        
        # 信号1: 双均线金叉(放宽条件)
        if sma10[-1] > sma30[-1] * 0.98:  # 允许2%的误差
            buy_signal = True
            logging.info(f"BUY SIGNAL: {symbol} - Golden Cross (SMA10:{sma10[-1]:.2f} > SMA30:{sma30[-1]:.2f})")
            
        # 信号2: RSI超卖(放宽条件)
        if rsi[-1] < 40:  # 放宽RSI超卖条件
            buy_signal = True
            logging.info(f"BUY SIGNAL: {symbol} - RSI Oversold (RSI:{rsi[-1]:.2f})")
            
        # 信号3: 布林带下轨突破(放宽条件)
        if close_price < lower[-1] * 1.05:  # 价格在下轨附近5%范围内
            buy_signal = True
            logging.info(f"BUY SIGNAL: {symbol} - Bollinger Breakout (Price:{close_price:.2f} < Lower:{lower[-1]:.2f})")
            
        # 卖出信号1: 双均线死叉
        if sma10[-1] < sma30[-1] and sma10[-2] >= sma30[-2]:
            sell_signal = True
            logging.info(f"SELL SIGNAL: {symbol} - Death Cross (SMA10:{sma10[-1]:.2f} < SMA30:{sma30[-1]:.2f})")
            
        # 卖出信号2: RSI超买
        if rsi[-1] > 70 and rsi[-2] <= 70:
            sell_signal = True
            logging.info(f"SELL SIGNAL: {symbol} - RSI Overbought (RSI:{rsi[-1]:.2f})")
            
        # 卖出信号3: 布林带上轨突破
        if close_price > upper[-1] and ctx.bars[-2]['close'] <= upper[-2]:
            sell_signal = True
            logging.info(f"SELL SIGNAL: {symbol} - Bollinger Breakout (Price:{close_price:.2f} > Upper:{upper[-1]:.2f})")
        
        # 执行交易
        if buy_signal and current_pos == 0:
            target_size = ctx.calc_target_size(0.05)  # 5%仓位
            ctx.buy_shares = target_size
            ctx.hold_bars = 20  # 持有20天
            ctx.stop_loss_pct = 8  # 8%止损
            ctx.stop_profit_pct = 15  # 15%止盈
            logging.info(f"EXECUTE BUY: {symbol} - Shares:{target_size} Price:{close_price:.2f}")
            
        if sell_signal and current_pos > 0:
            ctx.sell_shares = current_pos  # 平仓
            logging.info(f"EXECUTE SELL: {symbol} - Shares:{current_pos} Price:{close_price:.2f}")

    # 添加执行函数
    strategy.add_execution(execute, symbols)
    
    return strategy

# 运行策略
if __name__ == "__main__":

    
    # 创建并运行策略
    strategy = create_strategy()
    logging.info("策略创建完成，开始回测...")
    result = strategy.backtest(
        warmup=30,  # 30天预热期
        calc_bootstrap=True  # 计算bootstrap指标
    )
    
    # 打印结果
    print("回测完成")
    print(f"初始资金: 100000.00")
    print(f"交易次数: {len(result.trades)}")
    print("详细结果请查看result对象")
    
    # 保存交易记录
    result.trades.to_csv('trading_records.csv')